#include "StdAfx.h"
#include "IniParser.h"

const wchar_t UNICODE_BOM = 0xfeff;

const char JOINTOR_A[] = "_@@_";
const wchar_t JOINTOR_W[] = L"_@@_";
const TCHAR JOINTOR_T[] = _T("_@@_");

class MAKE_DICT : public CIniDisposer
{
public:
	MAKE_DICT(CIniParser::INIMAP &dict)
		: _dict(dict)
	{
		_dict.clear();
	}

	virtual BOOL ReceiveSection(CString strSection)
	{
		_strSection = strSection;
		return TRUE;
	}

	virtual BOOL ReceiveValue(CString strKey, CString strValue)
	{
		_dict.insert(make_pair((_strSection+JOINTOR_T+strKey).MakeUpper(), strValue));
		return TRUE;
	}

private:
	CIniParser::INIMAP &_dict;
	CString _strSection;
};

class FIND_VALUE : public CIniDisposer
{
public:
	FIND_VALUE(LPCTSTR lpSection, LPCTSTR lpKey, CString &strValue)
		: _strKey(lpKey)
		, _strSection(lpSection)
		, _strValue(strValue)
	{
		_strKey.Trim();
		_strSection.Trim();
	}

	virtual BOOL ReceiveSection(CString strSection)
	{
		return (strSection.CompareNoCase(_strSection) == 0);
	}

	virtual BOOL ReceiveValue(CString strKey, CString strValue)
	{
		if(strKey.CompareNoCase(_strKey) == 0)
		{
			_strValue = strValue;
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}

	CString& _strValue;

private:
	CString _strSection;
	CString _strKey;
};

CIniParser::CIniParser(void)
{
}

CIniParser::~CIniParser(void)
{
}

BOOL CIniParser::ParseIniFile(LPCTSTR lpszIniFileName)
{
	CFile iniFile;

	if(iniFile.Open(lpszIniFileName, CFile::modeRead))
	{
		Parse(&iniFile, &MAKE_DICT(dict));
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

void CIniParser::ParseIniInMem(LPVOID pIniData, unsigned int nBytes)
{
	CMemFile memFile(reinterpret_cast<BYTE *>(pIniData), nBytes);
	Parse(&memFile, &MAKE_DICT(dict));
}

BOOL CIniParser::ParseIniInResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType)
{
	HRSRC hResInfo = FindResource(hModule, lpName, lpType);
	if(hResInfo == NULL)
	{
		return FALSE;
	}

	HGLOBAL hResData = LoadResource(hModule, hResInfo);
	if(hResData == NULL)
	{
		return FALSE;
	}

	LPVOID pResData = LockResource(hResData);
	unsigned int nDataBytes = SizeofResource(hModule, hResInfo);

	ParseIniInMem(pResData, nDataBytes);

	return TRUE;
}

void CIniParser::Parse(CFile *pFile, CIniDisposer *pDisposer)
{
	wchar_t cBomTest = 0;
	pFile->Read(&cBomTest, sizeof(wchar_t));

	if(cBomTest == UNICODE_BOM)
	{
		Parse(pFile, pDisposer, L'W');
	}
	else
	{
		pFile->SeekToBegin();
		Parse(pFile, pDisposer, 'A');
	}
}

CString CIniParser::FetchValue(LPCTSTR lpSection, LPCTSTR lpKeyName, CFile *pFile)
{
	if(pFile)
	{
		pFile->SeekToBegin();
		CString strValue;
		Parse(pFile, &FIND_VALUE(lpSection, lpKeyName, strValue));
		return strValue;
	}
	else
	{
		TCHAR chKey = *lpKeyName;
		while(chKey != 0 && _istspace(chKey))
		{
			++lpKeyName;
			chKey = *lpKeyName;
		}

		CString searchKey = lpSection;
		searchKey.Trim();
		searchKey = searchKey + JOINTOR_T + lpKeyName;
		searchKey.TrimRight();
		searchKey.MakeUpper();

		INIMAP_IT it = dict.find(searchKey);
		if (it != dict.end())
		{
			return it->second;
		}
		else
		{
			return _T("");
		}
	}
}

UINT CIniParser::GetProfileInt(LPCTSTR lpSection, LPCTSTR lpKeyName, INT nDefault)
{
	return GetProfileInt(lpSection, lpKeyName, nDefault, NULL);
}

LPCTSTR CIniParser::GetProfileString(LPCTSTR lpSection, LPCTSTR lpKeyName, LPCTSTR lpDefault)
{
	CString strValue = FetchValue(lpSection, lpKeyName);

	if(strValue.IsEmpty() && lpDefault)
	{
		return lpDefault;
	}
	else
	{
		return strValue;
	}
}

void CIniParser::GetProfileString(LPCTSTR lpSection, LPCTSTR lpKeyName, LPCTSTR lpDefault, LPTSTR lpReturnedString,	DWORD nSize)
{
	LPCTSTR lpszValue = GetProfileString(lpSection, lpKeyName, lpDefault);

	_tcsncpy(lpReturnedString, lpszValue, nSize - 1);
	lpReturnedString[nSize - 1] = 0;
}

UINT CIniParser::GetProfileInt(LPCTSTR lpSection, LPCTSTR lpKeyName, INT nDefault, CFile *pFile)
{
	CString strValue = FetchValue(lpSection, lpKeyName, pFile);

	if(strValue.IsEmpty())
	{
		return nDefault;
	}
	else
	{
		return _ttoi(strValue);
	}
}

CString CIniParser::GetProfileString(LPCTSTR lpSection, LPCTSTR lpKeyName, LPCTSTR lpDefault, CFile *pFile)
{
	CString strValue = FetchValue(lpSection, lpKeyName, pFile);

	if(strValue.IsEmpty() && lpDefault)
	{
		return lpDefault;
	}
	else
	{
		return strValue;
	}
}
